/********************************************************************************
 * Copyright (c) 2019 [Open Lowcode SAS](https://openlowcode.com/)
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0 .
 *
 * SPDX-License-Identifier: EPL-2.0
 ********************************************************************************/

package org.openlowcode.server.data.properties;

import org.openlowcode.server.data.DataObject;
import org.openlowcode.server.data.DataObjectDefinition;
import org.openlowcode.server.graphic.SPage;
import org.openlowcode.server.runtime.OLcServer;
import org.openlowcode.server.runtime.SModule;
import org.openlowcode.tools.structure.ObjectDataElt;
import org.openlowcode.tools.structure.ObjectIdDataElt;
import org.openlowcode.tools.structure.ObjectIdInterface;

/**
 * a data object id encapsulates the unique id for an object generated by the
 * system.
 * 
 * @author <a href="https://openlowcode.com/" rel="nofollow">Open Lowcode
 *         SAS</a>
 *
 * @param <E> type of ID for the object
 */
public class DataObjectId<E extends DataObject<E>> implements ObjectIdInterface {
	/**
	 * @param definition definition of the data object
	 * @return a DataObjectId with a null payload
	 */
	public static <F extends DataObject<F>> DataObjectId<F> getNullDataObjectId(DataObjectDefinition<F> definition) {
		return new DataObjectId<F>(null, definition);
	}

	private String id;
	private DataObjectDefinition<E> definition;

	@Override
	public String getId() {
		return this.id;
	}

	/**
	 * @param id sets the id
	 */
	void setId(String id) {
		this.id = id;
	}

	/**
	 * @param id         id of the object
	 * @param definition definition of the object
	 */
	DataObjectId(String id, DataObjectDefinition<E> definition) {
		this.id = id;
		this.definition = definition;
	}

	/**
	 * @param id         id of the object
	 * @param definition definition of the object
	 * @return a data object id
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public static DataObjectId generateDataObjectId(String id, String objectid) {

		String[] splitobjectid = objectid.split(":");
		if (splitobjectid.length != 2)
			throw new RuntimeException(
					"objectid should have two components separated by ':', but does not have  " + objectid);
		String modulename = splitobjectid[0];
		SModule module = OLcServer.getServer().getModuleByName(modulename);
		String objectname = splitobjectid[1];
		DataObjectDefinition objectdefinition = module.getObjectDefinition(objectname);
		return new DataObjectId(id, objectdefinition);
	}

	/**
	 * @return the standard page page to show the object. This is to be called in
	 *         algorithms that are common to several object types
	 */
	@SuppressWarnings("rawtypes")
	public SPage getShowObjectPage() {
		String[] splitobjectid = this.getObjectId().split(":");
		if (splitobjectid.length != 2)
			throw new RuntimeException(
					"objectid should have two components separated by ':', but does not have  " + this.getObjectId());
		String modulename = splitobjectid[0];
		SModule module = OLcServer.getServer().getModuleByName(modulename);
		return module.getShowPageBasedOnObjectId((DataObjectId) this);
	}

	/**
	 * allows to get the object from the id in algorithms that are common to several
	 * object types
	 * 
	 * @return the object after it has been lookup up
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public E lookupObject() {
		String[] splitobjectid = this.getObjectId().split(":");
		if (splitobjectid.length != 2)
			throw new RuntimeException(
					"objectid should have two components separated by ':', but does not have  " + this.getObjectId());
		String modulename = splitobjectid[0];
		SModule module = OLcServer.getServer().getModuleByName(modulename);
		return (E) (module.getDataObjectBasedOnGenericId((DataObjectId) this));
	}

	/**
	 * transforms the generic DataObjectId into a casted DataObjectId
	 * 
	 * @param genericid        an uncasted id
	 * @param objectdefinition the data object definition
	 * @return the casted DataObjectId
	 */
	@SuppressWarnings("rawtypes")
	public static <E extends DataObject<E>> DataObjectId<E> castDataObjectId(DataObjectId genericid,
			DataObjectDefinition<E> objectdefinition) {

		return new DataObjectId<E>(genericid.id, objectdefinition);
	}

	/**
	 * creates a parsed data object id from an ObjectDataElt
	 * @param element data element
	 * @param definition definition of the  DataObject
	 * @return
	 */
	public static <E extends DataObject<E>> DataObjectId<E> generatefromDataObjectElt(ObjectDataElt element,
			DataObjectDefinition<E> definition) {

		return new DataObjectId<E>(element.getUID(), definition);
	}

	/**
	 * creates a parsed data object id from an ObjectIdDataElt
	 * @param element the  data element
	 * @return a generic DataObjectId
	 */
	@SuppressWarnings("rawtypes")
	public static DataObjectId generatefromDataObjectIdElt(ObjectIdDataElt element) {
		return generateDataObjectId(element.getId(), element.getObjectId());
	}

	/**
	 * creates a parsed data object id from an ObjectIdDataElt
	 * @param element  data element
	 * @param definition definition of the  DataObject
	 * @return a casted DataObjectId
	 */
	public static <E extends DataObject<E>> DataObjectId<E> generatefromDataObjectIdElt(ObjectIdDataElt element,
			DataObjectDefinition<E> definition) {

		return new DataObjectId<E>(element.getId(), definition);

	}

	@Override
	public String toString() {

		return "[DATAOBJECTID " + id + " " + this.getClass().getName() + "]";
	}

	@SuppressWarnings("rawtypes")
	@Override
	public boolean equals(Object otherobject) {
		if (otherobject instanceof DataObjectId) {
			DataObjectId otherid = (DataObjectId) otherobject;
			return otherid.getId().equals(this.id);
		}
		return false;
	}

	@Override
	public int hashCode() {

		return this.id.hashCode();
	}

	@Override
	public String getObjectId() {
		return definition.getModuleName() + ":" + definition.getName();
	}
}
